#include <types.h>
#include <sys_config.h>
#include <retcode.h>
#include <api/libc/string.h>
#include <api/libc/printf.h>
#include <osal/osal.h>
#include <hld/hld_dev.h>
#include <hld/dis/VPO.h>
#include <hld/decv/vdec_driver.h>
#include <hld/decv/decv.h>
#include <hld/deca/deca_dev.h>
#include <hld/deca/deca.h>
#include <hld/snd/snd_dev.h>
#include <hld/snd/snd.h>

#include <api/libimagedec/imagedec.h>
#include <api/libmp/pe.h>
#include "music/plugin.h"
#include "music/music_engine.h"
#include "image/image_engine.h"
#if 0
#include "image/plus720x576_420.h"

#include "image/h1_720x576h.264.h"
#endif

#define ENABLE_PE_CACHE
#ifdef NETWORK_SUPPORT
#include <api/libnet/libnet.h>
#include <api/libtcpip/lwip/inet.h>
extern FILE *url_open(const char *path, unsigned char* buffer, unsigned int buf_len, unsigned int offset);
extern size_t url_read(void *ptr, size_t size, size_t nmemb, FILE *stream);
extern size_t url_close(FILE * fp);
#endif
#ifdef MP4_CONTAINER
#include "av_player/av_player.h"
extern P_HANDLE g_mp4_av_player_handle;
#endif
struct pe_feature_cfg g_mp_feature_cfg;
static UINT8 pe_init_time = 0;
static struct pe_music_cfg g_pe_music_cfg;	//for release the memory in pe_cleanup

#ifdef SUPPORT_ES_PLAYER
FILE *ali_stream_open(const char *path, unsigned char* buffer, unsigned int buf_len, unsigned int offset);
size_t ali_stream_close(FILE * fp);
size_t ali_stream_read(void *ptr, size_t size, size_t nmemb, FILE *stream);
int ali_stream_seek(FILE *stream, off_t offset, int whence);
off_t ali_stream_tell(FILE *stream);
int is_ali_stream(char *file);
int is_es_stream(char *file);
int ali_stream_end(FILE *stream);
#endif
//################################################################################
//## wrapper for music engine

int music_init(struct pe_music_cfg *pe_music_cfg)
{
	return music_engine_init(pe_music_cfg);
}

void music_release()
{
	music_engine_release();
}

int music_play(char *filename)
{
	return music_engine_play(filename);
}

void music_seek(int time)
{
	music_engine_seek(time);
}

void music_stop(void)
{
	music_engine_stop();
}

void music_pause(void)
{
	return music_engine_pause();
}

int music_get_time(void)
{
	return music_engine_get_time();
}

void music_set_eq(int on, float preamp, float *bands)
{
	music_engine_set_eq(on, preamp, bands);
}

int music_get_song_info(char * filename, MusicInfo *music_info)
{
	return music_engine_get_song_info(filename, music_info);
}

int music_get_decoder_info(char * filename, DecoderInfo *decoder_info)
{
	return music_engine_get_decoder_info(filename, decoder_info);
}


//################################################################################
//## wrapper for image engine
static int image_init(struct pe_image_cfg *pe_image_cfg)
{
	int ret;
	
	ret = image_engine_init(pe_image_cfg);	

	return ret;
}

void image_restore_vpo_rect()
{
	struct Rect		main_src_rect;
	struct Rect		main_dst_rect;
	struct vpo_device *pvpo_sd;
	
	vpo_win_onoff((struct vpo_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_DIS),FALSE);
	pvpo_sd = (struct vpo_device *)dev_get_by_id(HLD_DEV_TYPE_DIS, 1);
	if(NULL != pvpo_sd)
		vpo_win_onoff(pvpo_sd, FALSE);	// avoid green screen

	osal_task_sleep(50);

	//restore the vop
	main_src_rect.uStartX = main_src_rect.uStartY = main_dst_rect.uStartX = main_dst_rect.uStartY = 0;
	main_src_rect.uWidth = PICTURE_WIDTH;
	main_src_rect.uHeight = PICTURE_HEIGHT;
	main_dst_rect.uWidth = SCREEN_WIDTH;
	main_dst_rect.uHeight = SCREEN_HEIGHT;
	vpo_zoom((struct vpo_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_DIS),&main_src_rect,&main_dst_rect);
	if(NULL != pvpo_sd)
		vpo_zoom((struct vpo_device *)dev_get_by_id(HLD_DEV_TYPE_DIS, 1),&main_src_rect,&main_dst_rect);

}
int image_cleanup()
{
	int ret;

	//cleanup the image decoder
	image_engine_abort();
	ret = image_engine_cleanup();
	return ret;
	
}

int image_abort()
{
	int ret ;

	ret = image_engine_abort();

	return ret;
}

int image_rotate(unsigned char rotate_angle)
{
	int ret;

	ret = image_engine_rotate(rotate_angle);

	return ret;
}

int image_zoom(struct Rect * dstRect, struct Rect * srcRect)
{
	int ret;
	ret = image_engine_zoom(dstRect, srcRect);

	return ret;
}


int image_2d_to_3d_swap(int display_type,void* pFunc2DTo3D)
{
	int ret;
	ret = image_engine_2d_to_3d_swap(display_type,pFunc2DTo3D);

	return ret;
}

int image_3d_user_option(int output_format, int user_depth, int user_shift, int user_vip)
{
	int ret;
    struct image_3d_para para;
    para.output_format = output_format;
    para.user_depth = user_depth;
    para.user_shift = user_shift;
    para.user_vip = user_vip;
	ret = image_engine_3d_user_option(&para);

	return ret;
}

int image_set_disp_param(int display_type, int output_format, int user_depth, int user_shift, int user_vip)
{
    int ret;
    struct image_3d_para para;
    para.output_format = output_format;
    para.user_depth = user_depth;
    para.user_shift = user_shift;
    para.user_vip = user_vip;
	ret = image_engine_set_disp_param(display_type, &para);

	return ret;
}


int image_decode(char *file, int mode, int left, int top, int width, int height, unsigned char rotate)
{
	int ret;
	struct image_config cfg;
	MEMSET(&cfg, 0, sizeof(cfg));

	if(mode == IMAGEDEC_REAL_SIZE)
	{
		cfg.decode_mode = IMAGEDEC_REAL_SIZE;
		cfg.src_top = top;
		cfg.src_left = left;
		cfg.src_width= width;
		cfg.src_height = height;
		cfg.rotate = ANG_ORI;
	}
	else if(mode == IMAGEDEC_THUMBNAIL)
	{
		cfg.decode_mode = IMAGEDEC_THUMBNAIL;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = ANG_ORI;		
	}
    else if (mode == IMAGEDEC_MULTI_PIC)
    {
        cfg.decode_mode = IMAGEDEC_MULTI_PIC;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = ANG_ORI;
	}
	else if(mode == IMAGEDEC_FULL_SRN)
	{
		cfg.decode_mode = IMAGEDEC_FULL_SRN;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = (rotate < 4) ? rotate :  ANG_ORI;
	}
	else if(mode == IMAGEDEC_SIZEDEFINE)
	{
		cfg.decode_mode = IMAGEDEC_SIZEDEFINE;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = (rotate < 4) ? rotate :  ANG_ORI;
	}
	else
	{
		cfg.decode_mode = IMAGEDEC_FULL_SRN;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = (rotate < 4) ? rotate :  ANG_ORI;
	}

	ret = image_engine_decode(file, &cfg);

	return ret;
	
	
}

int image_decode_ex(char *file, int mode, int left, int top, int width, int height, unsigned char rotate, struct image_slideshow_effect *effect)
{
	int ret;
	struct image_config cfg;
	MEMSET(&cfg, 0, sizeof(cfg));

	if(mode == IMAGEDEC_REAL_SIZE)
	{
		cfg.decode_mode = IMAGEDEC_REAL_SIZE;
		cfg.src_top = top;
		cfg.src_left = left;
		cfg.src_width= width;
		cfg.src_height = height;
		cfg.rotate = ANG_ORI;
	}
	else if(mode == IMAGEDEC_THUMBNAIL)
	{
		cfg.decode_mode = IMAGEDEC_THUMBNAIL;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = ANG_ORI;		
	}
    else if (mode == IMAGEDEC_MULTI_PIC)
    {
        cfg.decode_mode = IMAGEDEC_MULTI_PIC;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = ANG_ORI;
    }
	else
	{
		cfg.decode_mode = IMAGEDEC_FULL_SRN;
		cfg.dest_top = top;
		cfg.dest_left = left;
		cfg.dest_width= width;
		cfg.dest_height = height;
		cfg.rotate = (rotate < 4) ? rotate :  ANG_ORI;
		cfg.effect = effect;
	}

	ret = image_engine_decode(file, &cfg);

	return ret;
}

int image_display(char * file, struct image_display_t *pdisplay)
{
	int ret;

	pdisplay->dest_top = 0;
	pdisplay->dest_left = 0;
	pdisplay->dest_width = 720;
	pdisplay->dest_height = 576;

	struct image_config *cfg = (struct image_config *)pdisplay;

	ret = image_engine_decode(file, cfg);
	
	return ret;
}

int image_get_info(char *filename, struct image_info *info )
{
	int ret ;

	ret = image_engine_get_info(filename, info);

	return ret;
}

int image_ioctl (char *filename, UINT32 cmd, UINT32 para)
{
	int ret = 0;

	//ret = image_engine_ioctl(filename, cmd, para);

	return ret;
}

int pe_cfg(struct pe_feature_cfg *pcfg)
{
	if(!pcfg)
	{
		return -1;
	}

	MEMCPY((void*)&g_mp_feature_cfg, pcfg ,sizeof(struct pe_feature_cfg));
	
	return 0;
}
//################################################################################
//## wrapper for video engine

#ifdef MEDIA_PLAYER_VIDEO_SUPPORT

static int video_init(struct pe_video_cfg *pe_video_cfg)
{
	int ret;
	
	ret = video_engine_init(pe_video_cfg);	

	return ret;
}

int video_decode(char *file, UINT8 video_stream_type, enum SndDupChannel audio_channel, BOOL preview)
{
    /*
        Note:
        video_decode() run in CPU, video_engine_decode() is a remote call in dual-cpu architecture.
        video_engine_decode() need [char *file] be global memory. malloc it here is a general
        method for all projects, include single-cpu, dual-cpu, dual-cpu with hw security enabled.
        Q: why not malloc it in video_engine_decode()?
        A: video_engine_decode() run in SEE, CPU cannot read SEE memory
                                                                       -- Jerry Long on 2010-07-08
    */
    static char *m_pe_file_name_buf = NULL;
    if (m_pe_file_name_buf == NULL)
    {
        m_pe_file_name_buf = MALLOC(2048+4); // FULL_PATH_SIZE
        ASSERT(m_pe_file_name_buf);
        MEMSET(m_pe_file_name_buf, 0, 2048+4);
    }
    STRCPY(m_pe_file_name_buf, file);

    return video_engine_decode(m_pe_file_name_buf, video_stream_type, audio_channel, preview);
}

int video_dec_file(char *path, BOOL preview)
{
#ifdef SUPPORT_ES_PLAYER
	int ret = 0;

	if((ret = network_stream_test(path, preview)) > 0) 
		return ret;
#endif
	
#ifdef SUPPORT_MPEG4_TEST	
	return video_decode(path, UNKNOWN_STREAM, SND_DUP_NONE, preview);
#else
	return video_decode(path, MPEG_12_FILE_STREAM, SND_DUP_NONE, preview);
#endif
}

#endif


#ifdef SUPPORT_MPEG4_TEST
int video_set_output(enum VDecOutputMode eOutMode, struct VDecPIPInfo *pInitInfo)
{
    return video_engine_set_output(eOutMode, pInitInfo);
}
#endif
#ifdef MORETV_PLUGIN_SUPPORT
UINT8 pe_inited = 0;
struct pe_music_cfg pe_music_cfg_bk;
struct pe_image_cfg pe_image_cfg_bk;
struct pe_video_cfg pe_video_cfg_bk;
#endif

//########################################################################################
//## PE functions
int pe_init(struct pe_music_cfg *pe_music_cfg, struct pe_image_cfg *pe_image_cfg, struct pe_video_cfg *pe_video_cfg)
{
	int image_ret, music_ret, video_ret;


	if((NULL==pe_music_cfg)||(NULL==pe_image_cfg)||(NULL==pe_video_cfg))
	{
		return -1;
	}

	memset(&g_pe_music_cfg,0,sizeof(pe_music_cfg));
	
	//if app dont init this addr,then default config add by system
	if((0==pe_music_cfg->pe_cache_buf_start_addr)||(0==pe_music_cfg->pe_cache_buf_len))
	{
		pe_music_cfg->pe_cache_buf_len=MUSIC_PE_CACHE_SIZE_MAX;
		pe_music_cfg->pe_cache_buf_start_addr=MALLOC(pe_music_cfg->pe_cache_buf_len);
		if(NULL==pe_music_cfg->pe_cache_buf_start_addr)
		{
			return -1;
		}
		g_pe_music_cfg.pe_cache_buf_start_addr=pe_music_cfg->pe_cache_buf_start_addr;
		g_pe_music_cfg.pe_cache_buf_len=pe_music_cfg->pe_cache_buf_len;
	}

	
#ifdef MORETV_PLUGIN_SUPPORT
	//bk params
	MEMCPY(&pe_music_cfg_bk, pe_music_cfg, sizeof(struct pe_music_cfg));
	MEMCPY(&pe_image_cfg_bk, pe_image_cfg, sizeof(struct pe_image_cfg));
	MEMCPY(&pe_video_cfg_bk, pe_video_cfg, sizeof(struct pe_video_cfg));
	pe_inited = 1;
#endif
	if(pe_init_time !=0)
	{
		MEMSET((void*)&g_mp_feature_cfg, 0 ,sizeof(struct pe_feature_cfg));
		pe_init_time++;
		return 0;
	}

	MEMSET((void*)&g_mp_feature_cfg, 0 ,sizeof(struct pe_feature_cfg));
	//init the image decoder
	image_ret = image_init(pe_image_cfg);
	if (image_ret == -1)
	{
		return image_ret;
	}
	//init the music decoder
	music_ret = music_init(pe_music_cfg);
	if (music_ret == -1)
	{
		//image_cleanup();
	}
	
#ifdef MEDIA_PLAYER_VIDEO_SUPPORT

    video_ret = video_init(pe_video_cfg);
    if (video_ret == -1)
    {
		//image_cleanup();
		//music_release();
    }
	
	
#endif
#ifdef MP4_CONTAINER
	av_player_config player_config;
	MEMSET((void *)&player_config, 0, sizeof(av_player_config));		
	player_config.buf_start1 =AVP_BUF1_ADDR;//__MM_PVR_VOB_BUFFER_ADDR + 0XA00000;//__MM_FB_TOP_ADDR-0x2000000;// AVP_BUF1_ADDR;
	player_config.buf_size1 = AVP_BUF1_LEN;//0x1400000;//AVP_BUF2_LEN;//#define AVP_BUF1_LEN   0xF00000 //15M
	player_config.buf_start2 = AVP_BUF2_ADDR;//__MM_FB_TOP_ADDR - 0x2300000;//AVP_BUF2_ADDR;//0xA2000000;//pe_image_cfg->frm_y_addr;
	player_config.buf_size2 = AVP_BUF2_LEN;//0x2300000;//0xE00000;//0x2000000;//(pe_image_cfg->frm_y_size + pe_image_cfg->frm_c_size)<<2;#define AVP_BUF2_LEN   0x1400000//20M
	avp_cache_init(__MM_MP_BUFFER_ADDR);//
	av_player_init(&player_config);
	g_mp4_av_player_handle = av_player_create();
#endif

#ifdef ENABLE_PE_CACHE
	pe_cache_init();
    
	pe_cache_cb_fp fp;
    
#ifdef NETWORK_SUPPORT//net work

	fp.file_open = url_open;
	fp.file_close = url_close;
	fp.file_eof = NULL;
	fp.file_read = url_read;
	fp.file_seek = NULL;
	fp.file_tell = NULL;
	pe_cache_register_net_fp(fp,PE_NET_FILE_TYPE_URL);		
#endif

#ifdef SUPPORT_ES_PLAYER
	fp.file_open = ali_stream_open;
	fp.file_close = ali_stream_close;
	fp.file_eof = ali_stream_end;
	fp.file_read = ali_stream_read;
	fp.file_seek = ali_stream_seek;
	fp.file_tell = ali_stream_tell;
	pe_cache_register_net_fp(fp,PE_ALI_SPECIAL_STREAM);	
#endif

#endif

	if(image_ret == 0 && (music_ret == 0 || video_ret == 0))
		pe_init_time++;
	return (music_ret||video_ret);
}

int pe_cleanup()
{
	//release the image decoder
	#if 1
	//don't release pe when usb plug out
	if(pe_init_time==1)
	{
		image_cleanup();
		//release the music decoder
		music_release();
		pe_init_time=0;
		mpg_cmd_stop_proc();

	    pe_cache_release();        
	}
	else
	{
	    if(pe_init_time!=0)
    		pe_init_time--;
		//do nothing some module still need pe
	}

	if(NULL!=g_pe_music_cfg.pe_cache_buf_start_addr)
	{
		g_pe_music_cfg.pe_cache_buf_len=0;
		FREE(g_pe_music_cfg.pe_cache_buf_start_addr);
		g_pe_music_cfg.pe_cache_buf_start_addr=NULL;
	}
	#else
	
	image_cleanup();
	//release the music decoder
	music_release();
	#endif
}
#ifdef MORETV_PLUGIN_SUPPORT
int pe_reinit()
{
	///if(pe_inited)
		pe_init(&pe_music_cfg_bk, &pe_image_cfg_bk, &pe_video_cfg_bk);
	return 0;
}
#endif

int get_file_name_ext(const char *filename, char *pext, int ext_size)
{
    int file_name_len;
    int i;
    file_name_len = strlen(filename);

    if (file_name_len <= 3)
    {
        *pext = 0;
        return 0;
    }
    for ( i = file_name_len; i; i--)
    {
        if (filename[i-1] == '.')
            break;
    }

    if (i == 0)
    {
        *pext = 0;
        return 0;
    }

    strncpy(pext, &filename[i], ext_size - 1);
    for (i = 0; pext[i]; i++)
    {
        if (pext[i] >= 'A' && pext[i] <= 'Z')
        {
            pext[i] |= 0x20;
        }
    }

    return i;
}

